/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ThreadBasedSimulator;

/**
 *
 * @author mahesh
 */

public class MemoryThread extends Thread{

    public MemoryThread()
    {
        m_MemoryData = new int[2][8192];
        m_MAR = 0;
        m_MDR = 0;
        m_nOperation = 0;
        m_bToBeExecuted = false;

        READ_BYTE  = 0;
        READ_WORD  = 1;
        READ_BLOCK = 2;
        WRITE_BYTE = 3;
        WRITE_WORD = 4;
        WRITE_BLOCK = 5;
    }
    
    public MemoryThread( Clock clk )
    {
        m_clock = clk;
        m_MemoryData = new int[2][8192];
        m_MAR = 0;
        m_MDR = 0;
        m_nOperation = 0;
        m_bToBeExecuted = false;

        READ_BYTE  = 0;
        READ_WORD  = 1;
        READ_BLOCK = 2;
        WRITE_BYTE = 3;
        WRITE_WORD = 4;
        WRITE_BLOCK = 5;

    }
    
    ///////////////////////////////////////////////////////////
    //Set of function that the processor can call
    ///////////////////////////////////////////////////////////
    void SetOperation( int nOP )
    {
        m_nOperation = nOP;
        SetExecutionFlag();
    }
    
    void ResetExecutionFlag()
    {
        m_bToBeExecuted = false;
    }
    
    void SetExecutionFlag()
    {
        m_bToBeExecuted = true;
    }
    
    void SetMAR( int nMemAddr )
    {
        m_MAR = nMemAddr;
    }
    
    void SetMDR( int nMemData )
    {
        m_MDR = nMemData;
    }
    void SetBlockSize( int nSize ) 
    {
        m_nBlockSize = nSize;
    }
    
    int GetMDR()
    {
        return m_MDR;
    }
    int GetMAR()
    {
        return m_MAR;
    }
    
    String GetBlockBuffer()
    {
        return m_sBlockBuffer;
    }
    
    void SetBlockBuffer( String sData )
    {
        m_sBlockBuffer = sData;
    }
    
    boolean HasCompleted()
    {
        boolean retval;
        
        retval = !m_bToBeExecuted;
        
        return retval;
    }
    // This version of write is to be used only while booting up
    void WriteUnsynchronized( int nVal, int nAddr )
    {
        // while writing words, we put lower order byte first.
        // also make sure you are incrementing addresses by 2      
        System.err.println("Writeunsynchronized : " + nVal + " " + nAddr);
        m_MemoryData[0][ nAddr/2 ] = ( nVal % 256 );
        m_MemoryData[1][ nAddr/2 ] = ( nVal / 256 );
    }
    
    ///////////////////////////////////////////////////////////
    // The actual fetching and writing functions
    ///////////////////////////////////////////////////////////
    void FetchByte()
    {
        int nBankNum = m_MAR % 2;
        m_MDR = m_MemoryData[nBankNum][ m_MAR / 2 ];
    }
    void FetchWord()
    {
        
        m_MDR = m_MemoryData[0][ m_MAR / 2 ] + 
                m_MemoryData[1][ m_MAR / 2 ] * 256;
    }
    void FetchBlock()
    {
        
        m_sBlockBuffer = "";
        for( int ii = m_MAR; ii < m_MAR + m_nBlockSize; ii++ )
        {
            int nBankNum = ii % 2;
            m_sBlockBuffer += (char)m_MemoryData[nBankNum][ ii / 2 ];
        }
        
    }
    
    void WriteWord()
    {
        m_MemoryData[0][ m_MAR / 2 ]   = (m_MDR % 256);
        m_MemoryData[1][ m_MAR / 2 ]   = (m_MDR / 256);
    }
    
    void WriteByte()
    {
        int nBankNum = m_MAR % 2;
        m_MemoryData[nBankNum][ m_MAR / 2 ] = (char) (m_MDR);
    }
    
    void WriteBlock()
    {
        for( int ii = m_MAR; ii < m_sBlockBuffer.length(); ii++ )
        {
            int nBankNum = ii % 2;
            m_MemoryData[ nBankNum ][ ii /2 ] = m_sBlockBuffer.charAt(ii - m_MAR);
        }
    }
    ///////////////////////////////////////////////////////////
    // The main thread for the memory
    ///////////////////////////////////////////////////////////
    @Override
    public void run() {
        
        while( true )
        { 
           synchronized( m_clock )
            {
                try {
                    
                    m_clock.wait();
                } catch (InterruptedException ex) {
                    System.out.println( "Exception in MemoryThread.wait : "+ ex );
                }
            }
            
            // at this point we have the clock's permission to go on        
            if( m_bToBeExecuted == true )
            {
                System.err.println(" MemoryThread, operation = " + m_nOperation );
                switch( m_nOperation )
                {
                    case 0:
                        FetchByte();
                        break;
                    case 1:
                        FetchWord();
                        break;
                    case 2:
                        FetchBlock();
                        break;
                    case 3:
                        WriteByte();
                        break;
                    case 4:
                        WriteWord();
                        break;
                    case 5:
                        WriteBlock();
                        break;
                        
                }
                
                ResetExecutionFlag();
            }
                
        }
    }

    // The memory has 4096 words or 8192 bytes
    // For simplicity, we assume that the memory is byte/word addressable
    int     m_MemoryData[][];
    
    // The memory has 2 registers
    // MAR (for address )and MDR (for data).
    int     m_MAR;
    int     m_MDR;
    
    String  m_sBlockBuffer; // used for block operations
    int     m_nBlockSize;
    // It uses a dirty bit to decide, 
    // if the current command is to be executed
    boolean m_bToBeExecuted;
    
    // And another variable to decide if it is a read or write
    // m_nOperation == 0 => read byte
    // m_nOperation == 1 => read word
    // m_nOperation == 2 => write byte
    // m_nOperation == 3 => write word    
    int m_nOperation;
    
    // It also needs a reference to a clock
    Clock m_clock;
    
    // last but not the least, the set of constants that are needed
    // by outside classes
    public static int READ_BYTE;
    public static int READ_WORD;
    public static int READ_BLOCK;
    public static int WRITE_BYTE;
    public static int WRITE_WORD;
    public static int WRITE_BLOCK;
}
